<?php

/**
 * CCoreLabsPop3Server
 *
 * @author Usenko Timur <usenko.timur@gmail.com>
 * @package labs
 */

/**
 * @package labs
 */
class CCoreLabsPop3Server
{
	#<editor-fold defaultstate="collapsed" desc="constants">
	const OK = '+OK';
	const ERR = '-ERR';
	const END_DOT = '.';

	const CRLF = "\r\n";

	const CACHE_FILE = '.system.cache';
	#</editor-fold>
	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @var string
	 */
	private $_sFilePathToPopStorage;
	#</editor-fold>

	/**
	 * @param string $sFilePathToPop3Storage
	 * @return CCoreLabsPop3Server
	 */
	public function __construct($sFilePathToPop3Storage)
	{
		$this->_sFilePathToPopStorage = trim($sFilePathToPop3Storage);
	}

	/**
	 * @param string $sCommandLine
	 * @return string
	 */
	public function Response($sCommandLine)
	{
		$aCommandArray = explode(' ', trim($sCommandLine));
		$sCommand = trim($aCommandArray[0]);

		$sFirstArgument = isset($aCommandArray[1]) ? trim($aCommandArray[1]) : null;
		$sSecondArgument = isset($aCommandArray[2]) ? trim($aCommandArray[2]) : null;
		
		$aResponse = array();
		switch ($sCommand)
		{
			case '':
			case 'USER':
			case 'PASS':
			case 'NOOP':
			case 'RSET':
			case 'QUIT':
				$aResponse[] = self::OK.$this->_getSuccesResponseTextFromCommand($sCommand);
				break;
			case 'RETR':
				if (null !== $sFirstArgument && (int) $sFirstArgument > 0)
				{
					$aStorageData = $this->_initStorageFolderData();
					if ($aStorageData)
					{
						foreach ($aStorageData['list'] as $aListItem)
						{
							if ((int) $sFirstArgument === (int) $aListItem['index'])
							{
								if (file_exists($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$aListItem['uid']))
								{
									$aFile = file($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$aListItem['uid']);
									if (is_array($aFile))
									{
										$aResponse[] = self::OK.' '.$aListItem['size'].' octets follow.';
										foreach ($aFile as $sLine)
										{
											$sLine = rtrim($sLine, self::CRLF);
											$sLine = ('.' === substr($sLine, 0, 1)) ? '.'.$sLine : $sLine;
											$aResponse[] = $sLine;
										}
										$aResponse[] = self::END_DOT;
									}
								}
								break;
							}
						}
					}
				}
				break;
			case 'TOP':
				if (null !== $sFirstArgument && (int) $sFirstArgument > 0)
				{
					$aStorageData = $this->_initStorageFolderData();
					if ($aStorageData)
					{
						foreach ($aStorageData['list'] as $aListItem)
						{
							if ((int) $sFirstArgument === (int) $aListItem['index'])
							{
								if (file_exists($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$aListItem['uid']))
								{
									$aFile = file($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$aListItem['uid']);
									if (is_array($aFile))
									{
										$iBackLineCount = (null !== $sSecondArgument && (int) $sSecondArgument > 0)
											? (int) $sSecondArgument : 0;
										
										$bIsHeaderSection = true;
										
										$aResponse[] = self::OK.' '.$aListItem['size'].' octets follow.';
										foreach ($aFile as $sLine)
										{
											$sLine = rtrim($sLine, self::CRLF);
											$sLine = ('.' === substr($sLine, 0, 1)) ? '.'.$sLine : $sLine;
											
											if ($bIsHeaderSection || $iBackLineCount > 0)
											{
												$aResponse[] = $sLine;
											}
											
											if (!$bIsHeaderSection && $iBackLineCount > 0)
											{
												$iBackLineCount--;
											}
											
											if ($bIsHeaderSection && '' === $sLine)
											{
												$bIsHeaderSection  = false;
											}
										}
										$aResponse[] = self::END_DOT;
									}
								}
								break;
							}
						}
					}
				}
				break;
			case 'DELE':
				/**/
				$aResponse[] = self::OK.$this->_getSuccesResponseTextFromCommand($sCommand);
				break;
				/**
				if (null !== $sFirstArgument && (int) $sFirstArgument > 0)
				{
					$aStorageData = $this->_initStorageFolderData();
					if ($aStorageData)
					{
						foreach ($aStorageData['list'] as $aListItem)
						{
							if ((int) $sFirstArgument === (int) $aListItem['index'])
							{
								if(@unlink($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$aListItem['uid']))
								{
									@unlink($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.self::CACHE_FILE)
									$aResponse[] = self::OK.$this->_getSuccesResponseTextFromCommand($sCommand);
								}
								break;
							}
						}
					}
				}
				break;
				/**/
			case 'STAT':
				$aStorageData = $this->_initStorageFolderData();
				if ($aStorageData)
				{
					$aResponse[] = self::OK.' '.$aStorageData['count'].' '.$aStorageData['size'];
				}
				break;
			case 'UIDL':
				$aStorageData = $this->_initStorageFolderData();
				if ($aStorageData)
				{
					if (null === $sFirstArgument)
					{
						$aResponse[] = self::OK;
						foreach ($aStorageData['list'] as $aListItem)
						{
							$aResponse[] = $aListItem['index'].' '.$aListItem['uid'];
						}
						$aResponse[] = self::END_DOT;
					}
					else
					{
						$sResponseAdd = self::ERR;
						foreach ($aStorageData['list'] as $aListItem)
						{
							if ((int) $sFirstArgument === (int) $aListItem['index'])
							{
								$sResponseAdd = self::OK.' '.$aListItem['index'].' '.$aListItem['uid'];
								break;
							}
						}
						$aResponse[] = $sResponseAdd;
					}
				}
				break;
			case 'LIST':
				$aStorageData = $this->_initStorageFolderData();
				if ($aStorageData)
				{
					if (null === $sFirstArgument)
					{
						$aResponse[] = self::OK;
						foreach ($aStorageData['list'] as $aListItem)
						{
							$aResponse[] = $aListItem['index'].' '.$aListItem['size'];
						}
						$aResponse[] = self::END_DOT;
					}
					else
					{
						$sResponseAdd = self::ERR;
						foreach ($aStorageData['list'] as $aListItem)
						{
							if ((int) $sFirstArgument === (int) $aListItem['index'])
							{
								$sResponseAdd = self::OK.' '.$aListItem['index'].' '.$aListItem['size'];
								break;
							}
						}
						$aResponse[] = $sResponseAdd;
					}
				}
				break;
			default:
				$aResponse[] = self::ERR.' [unknown command: '.$sCommand.']';
				break;
		}

		if (0 === count($aResponse))
		{
			$aResponse[] = self::ERR;
		}
		
		return implode(self::CRLF, $aResponse).self::CRLF;
	}

	/**
	 * @param string $sCommand
	 * @return string
	 */
	private function _getSuccesResponseTextFromCommand($sCommand)
	{
		switch ($sCommand)
		{
			case '':
				return ' Hello there.';
			case 'USER':
				return ' Password required.';
			case 'PASS':
				return ' logged in.';
			case 'NOOP':
				return ' Yup.';
			case 'RSET':
				return ' Resurrected.';
			case 'QUIT':
				return ' Bye-bye.';
			case 'DELE':
				return ' Done.';
		}
		
		return '';
	}

	/**
	 * @return array | false
	 */
	private function _initStorageFolderData()
	{
		if (@file_exists($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.self::CACHE_FILE))
		{
			return @unserialize(file_get_contents($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.self::CACHE_FILE));
		}

		if (is_dir($this->_sFilePathToPopStorage))
		{
			$iCount = 0;
			$iSizes = 0;
			$aList = array();
			$oDir = dir($this->_sFilePathToPopStorage);
			while (false !== ($sFileItem = $oDir->read()))
			{
				if (is_file($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$sFileItem) && $sFileItem !== self::CACHE_FILE)
				{
					$iSize = filesize($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.$sFileItem);
					$aList[] = array(
						'index' => $iCount + 1,
						'uid' => $sFileItem,
						'size' => $iSize
					);
					$iCount++;
					$iSizes += $iSize;
				}
			}

			$aReturn = array('count' => $iCount, 'size' => $iSizes, 'list' => $aList);
			@file_put_contents($this->_sFilePathToPopStorage.DIRECTORY_SEPARATOR.self::CACHE_FILE, @serialize($aReturn));
			return $aReturn;
		}
		
		return false;
	}
}